package com.hhuhh.smarthome.socket.udp.api;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import com.hhuhh.smarthome.socket.AcceptorCallback;
import com.hhuhh.smarthome.socket.IoConstants;
import com.hhuhh.smarthome.socket.SocketDataRSA;
import com.hhuhh.smarthome.socket.model.SimpleData;
import com.hhuhh.smarthome.socket.support.ResultDecoder;

/**
 * @author: John zhang
 * @version: 0.1
 */
public class ApiConnectorIoHandler extends IoHandlerAdapter implements SocketDataRSA {
	private static final String tag = ApiConnectorIoHandler.class.getSimpleName ();
	
	public static HashMap<String, AcceptorCallback<SimpleData>> acceptors;
	
	private PublicKey serverPubKey; // 服务端公钥
	private PublicKey pubKey; // 客户端公钥
	private PrivateKey priKey; // 客户端私钥
	private boolean useRSA; // 是否使用RSA加密
	private ArrayDeque<String> actionQueues = new ArrayDeque<String> ();
	
	@Override
	public void messageSent( IoSession session, Object message ) throws Exception {
		if( IoConstants.DEBUG ) {
			System.out.println( String.format ( "sent : { action=%s, content=%s ", session.getAttribute ( IoConstants.REQUEST_ACTION_KEY ), message ) );
		}
		actionQueues.add ( (String) session.getAttribute ( IoConstants.REQUEST_ACTION_KEY ) ) ;
	}
	
	@Override
	public void messageReceived( IoSession session, Object message ) throws Exception {
		byte[] srcData = (byte[]) message;
		byte[] pubKeyAction = Arrays.copyOf ( srcData, (IoConstants.ACTION_PUBLIC_KEY).length () );
		String action = null;
		Object datas = srcData;
		if( (IoConstants.ACTION_PUBLIC_KEY).equals ( new String( pubKeyAction ) )) {
			action = IoConstants.ACTION_PUBLIC_KEY;
			datas = Arrays.copyOfRange ( srcData, (IoConstants.ACTION_PUBLIC_KEY + IoConstants.ACTION_DATA_DELIMITER).length (), srcData.length );
		}
		
		SimpleData resp = new SimpleData ();
		ResultDecoder[] resultDecoders = (ResultDecoder[]) session.getAttribute ( IoConstants.RESULT_DECODER_KEY );
		for( ResultDecoder decoder : resultDecoders ) {
			if( decoder == ResultDecoder.RSA ) {
				resp.setRSAPriKey ( getPriKey() );
			}
		}
		
		if( resultDecoders != null && resultDecoders.length != 0 ) {
			resp.wrapperResult ( datas, resultDecoders );
		}
		else {
			resp.wrapperResult ( datas );
		}
		
		if( action == null ) {
			action = resp.getAction();
		}
		AcceptorCallback<SimpleData> callback = (AcceptorCallback<SimpleData>) acceptors.get ( action );
		if( callback != null ) {
			if( actionQueues.contains ( action ) )
				actionQueues.remove ( action );
			callback.onResult ( resp );
		}
	}
	
	@Override
	public void exceptionCaught( IoSession session, Throwable cause ) {
		session.close ( true );
	}

	@Override
	public void sessionIdle( IoSession session, IdleStatus status ) throws Exception {
		if( actionQueues.isEmpty () )
			return;
		
		String actionName = actionQueues.poll ();
		AcceptorCallback<?> callback = acceptors.get ( actionName );
		if( callback != null ) {
			if( IoConstants.DEBUG ) {
				System.out.println( "服务端响应超时.." );
			}
			callback.readerOrWriterIdleHandler ();
		}
	}
	
	@Override
	public void sessionClosed( IoSession session ) throws Exception {
		System.out.println( "session被关闭." );
		serverPubKey = null;
		pubKey = null;
		priKey = null;
	}
	
	/**
	 * 是否使用RSA对数据进行加密/解密
	 * @param val
	 */
	public void setUseRSA( boolean val ) {
		setUseRSA ( val, null, null );
	}
	
	/**
	 * 是否使用RSA对数据进行加密/解密, 如果启用RSA加密, 则需要设置私钥与公钥, 否则会抛出异常
	 * @param val 是否启用
	 * @param priKey 客户端私钥
	 * @param pubKey 客户端公钥
	 */
	public void setUseRSA( boolean val, PublicKey pubKey, PrivateKey priKey ) {
		if( val && ( priKey == null || pubKey == null ) ) {
			throw new IllegalArgumentException ( "If you use RSA encrypt or decrypt datas, please invoked setPrivateKey() and setPublicKey()" );
		} 
		this.useRSA = val;
		this.priKey = priKey;
		this.pubKey = pubKey;
	}
	
	public boolean isUseRSA() {
		return useRSA;
	}
	
	/**
	 * 添加消息接收回调函数
	 * @param key
	 * @param callback
	 */
	public void addCallback( String key, AcceptorCallback<SimpleData> callback ) {
		if( acceptors == null ) {
			acceptors = new HashMap<String, AcceptorCallback<SimpleData>>();
		}
		
		@SuppressWarnings( "unused" )
		AcceptorCallback<?> oldAcceptor = acceptors.put ( key, callback );
		oldAcceptor = null;
	}

	@Override
	public RSAPublicKey getClientPubKey() {
		return (RSAPublicKey) pubKey;
	}

	@Override
	public RSAPrivateKey getPriKey() {
		return (RSAPrivateKey) priKey;
	}

	@Override
	public RSAPublicKey getServerPubKey() {
		return (RSAPublicKey) serverPubKey;
	}
	
	public void setServerPubKey( PublicKey serverPubKey ) {
		if( serverPubKey == null ) {
			throw new IllegalArgumentException ( "serverPubKey must cannot be null!" );
		}
		this.serverPubKey = (RSAPublicKey) serverPubKey;
	}
	
}